﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;

namespace Mtidata0225
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        string str = " ";
       // byte[] buf = new byte[5000];    //存放最终的数据考虑用动态的来实现？？
        List<byte> buf=new List<byte>();
        int l = 0;
        int len = 0;                   //用在数据图形绘制函数中
        bool serialportcom = false;    //串口初始值
        bool baudrate = false;        //波特率设置
        bool bgndispflag = false;    //开始显示标志位
        bool setflag = false;      //对应的按键为“确认”
        bool sendflag = false;     //用在数据发送区，确认发送指令
        string dispstyle = "十六进制";    //默认16进制显示
      //  string dispstyle = "";
        uint checksum = 0;
        byte MID = 0;
        byte datalength = 0;            //数据帧初始长度为0
        uint sum = 0;
        List<double> mylist=new List<double>();   //容量可变的容器

        /// <summary>
        /// 串口设置程序
        /// </summary>
        /// <param name="sender"></param> 
        /// <param name="e"></param>
        private void confirmbtn_Click(object sender, EventArgs e)
        {
            if (portnamelbe.Text != "")
                serialportcom = true;
            else
                serialportcom = false;

            if (baudratelble.Text != "")
                baudrate = true;
            else
                baudrate = false;

            if (baudrate == false || serialportcom == false)
                MessageBox.Show("请先对串口进行设置，并按确认键", "系统提示");
            else                  //true at the same time 
            {
                setflag = true;
                if (serialPort1.IsOpen == false)
                {
                    serialPort1.PortName = Convert.ToString(portnamelbe.Text);
                    serialPort1.BaudRate = Convert.ToInt32(baudratelble.Text);
                  //  serialPort1.Open();        //打开串口
                }
               // drawaxis();
            }
        }

        /// <summary>
        /// 设置显示格式以及开始显示
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bgndispbtn_Click(object sender, EventArgs e)
        {
            if (setflag == false)
            {
                MessageBox.Show("请先对串口进行设置,并按确认键", "系统提示");
                return;
            }
           // bgndispflag = true;
            serialPort1.Open();
        }

        /// <summary>
        /// 指令发送程序
        /// 为什么发送的时候自动添加了0x0a了呢？
        /// 为什么不能实现指令的发送呢？？
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void sendbtn_Click(object sender, EventArgs e)
        {                        //为什么不能给MTI发送指令呢????
            if (setflag == false)     //先检查串口的基本设置是否正确
            {
                MessageBox.Show("请先对串口进行设置,并按确认键", "系统提示");
                return;
            }

            if (richTextBox2.Text == "")
            {
                MessageBox.Show("请输入发送内容", "系统提示");
                return;
            }
            sendflag = true;
            if (serialPort1.IsOpen == false)
                serialPort1.Open();
           // byte[] data = Encoding.Unicode.GetBytes(richTextBox2.Text);
           // string sendstr = Convert.ToBase64String(data);  //解码时也必须是基于Base64
            //serialPort1.WriteLine(richTextBox2.Text);   //发送内容   //否则的话会出错
            serialPort1.Write(richTextBox2.Text);
            //MessageBox.Show("数据发送成功！", "系统提示");

        }

        /// <summary>
        /// 显示清空按键，将所有的显示内容清空
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void clcbtn_Click(object sender, EventArgs e)
        {
            str = "";
            richTextBox1.Text = "";
            l = 0;
        }

        /// <summary>
        /// 数据接收触发串口程序
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {    //为什么重新上电后才能使用？？(有可能是原先的MTmanager继续占用端口)
             int n = serialPort1.BytesToRead;//读取当前有多少数据可读
             byte[] buffer = new byte[n];    //新建内容
             byte[] databuffer = new byte[51];
             char[] str1 = { 'a', 'b', 'c', 'd', 'e', 'f' };
             int gao = 0, di = 0;
             serialPort1.Read(buffer, 0, n);   //读取缓冲区
           //  Array.Copy(buffer,0,buf,l,buffer.Length);   //将所有的数据复制到buf中
             for (uint k = 0; k < buffer.Length; k++)
                 buf.Add(buffer[k]);
              //   l += buffer.Length;

             if (decirbtn.Checked == true)  //判断使用哪种方式进行显示
                 dispstyle = decirbtn.Text;
             if (hexrbtn.Checked == true)
                 dispstyle = hexrbtn.Text;
             switch(dispstyle)             //数据显示区显示的数据格式
             {
                 case "十六进制":
                 {
                     str = "";
                     for (uint j = 0; j < buffer.Length; j++)  //16进制显示
                     {
                         di = buffer[j] & 0x0f;
                         gao = buffer[j] & 0xf0;
                         gao = gao >> 4;
                         if (gao >= 0 & gao <= 9)
                             str += Convert.ToString(gao);
                         else
                             str += Convert.ToString(str1[gao - 10]);
                         if (di >= 0 & di <= 9)
                             str += Convert.ToString(di);
                         else
                             str += Convert.ToString(str1[di - 10]);
                         str += " ";
                     }
                 }break;
                 case"十进制":
                 {
                     str = "";
                     for (uint j = 0; j < buffer.Length; j++)
                     {
                         str += Convert.ToString(buffer[j]);
                         str += " ";
                     }
                 }break;
                 default:break;
             }
             this.richTextBox1.Invoke
             (
                 new MethodInvoker     //线程切换
                 (
                   delegate
                   {
                       this.richTextBox1.AppendText(str);
                   }

                 )
             );
           //  if (buf.Count >= 3000)   //是不是也要对buf及时的清空，否则的话占内存太多
            // {
           //      l = 0;
               //  buf.Clear();
              //   str = "";       //清空缓存区
             //    serialPort1.Close();
             if (buf.Count >= 1000)
             {
                 for (int i = 0; i < buf.Count; i++)//应该放在最后数据采集完后再处理……
                 {
                     if (buf[i] != 0xfa)   //一直在检索，直到找到报头
                         continue;
                     else                       //找到报头的话
                     {
                         if (buf[i + 1] != 0xff)    //起始位
                             continue;      //此处是返回到哪一级,返回到for那一级
                         else                //返回到上一级循环
                         {
                             i += 2;
                             MID = buf[i];
                             i++;
                             datalength = buf[i];
                         }
                     }
                     //  Array.Copy(buf, i + 1, databuffer, 0, datalength); //把整个数据区
                     // buf.ToArray(databuffer);
                     for (int m = i + 1; (m < i + 1 + datalength) && (m < buf.Count); m++)
                         databuffer[m - i - 1] = buf[m];
                     //  databuffer = (byte[])buf.ToArray(typeof(byte));
                     for (uint k = 0; k < databuffer.Length; k++)      //复制进去
                         sum += databuffer[k];
                     sum += 0xff;
                     sum += MID;
                     i += (datalength + 1);   //下标运算很麻烦，很容易出错*****
                     if (i >= buf.Count)
                         break;
                     checksum = buf[i];
                     sum += checksum;
                     sum += datalength;
                     if ((sum & 0x00ff) == 0)    //校验和
                     {
                         caculate(databuffer);
                         sum = 0;
                         buf.Clear();   //这一句的位置很重要
                     }
                     else
                         continue;
                 }
               //  buf.Clear();     //前面的运算进行完后清空内存   
             }
        }


        /// <summary>
        /// IEEE754标准，将4字节的数据转换成单精度浮点型数据
        /// </summary>
        public  void caculate(byte[] data)    //将16进制按照IEEE754标准进行转换
        {    //public,static区别对于调用其它的很重要
            sbyte signalbit = 1;    //默认符号位，默认时候为正数
            byte zhishu = 0;          //指数上的值
            double M_value = 0;         //后23位数据的值
            double acccurvedata=0;      //存放z向加速度的值
            UInt32 datatmp = 0;
            UInt32 datatmp1 = 0;
            double[] finalvalue = new double[12];       //最后的输出值
            for (uint i = 0; i < data.Length-3; i += 4) //由于数据区最后三位为TS，status
            {                                       //计算的时候不用，所以不需要。
                if ((data[i] & 0x80) == 0x80)
                    signalbit = -1;         //第一位为1则表示为负数
                else
                    signalbit = 1;         //otherwise positive
                datatmp = Convert.ToUInt32(data[i] & 0x7f);      //shift left
                datatmp <<= 8;
                datatmp = datatmp | data[i + 1];
                datatmp = datatmp >> 7;
                zhishu = Convert.ToByte(datatmp);     //求出指数部分大小

                datatmp = 0;
                datatmp = Convert.ToUInt32(data[i + 1] & 0x7f);
                datatmp <<= 16;
                datatmp |= data[i + 3];
                datatmp1 = data[i + 2];
                datatmp1 <<= 8;
                datatmp |= datatmp1;
                datatmp1 = datatmp;
                UInt32 shift = 0x0001;
                for (uint j = 0; j < 23; j++)
                {
                    if ((datatmp & shift) != 0)
                    {
                        M_value += (1 / Math.Pow(2, 23 - j));
                        shift <<= 1;
                    }
                    else
                    {
                        shift <<= 1;
                        continue;
                    }
                }
                finalvalue[i / 4] =Convert.ToDouble(( (1 + M_value) //所有数据保留小数点后8位
                    * Math.Pow(2, zhishu - 127) * signalbit).ToString("#.000000000000"));
                M_value = 0;
                datatmp = 0;
                datatmp1 = 0;
                if (finalvalue[11] != 0)   //这一次数据全采集完了才显示
                {
                    datadisp(finalvalue);      //没必要实时显示数值吧？这样占用的时间太多
                    acccurvedata = finalvalue[2];    //z向加速度
                    drawcurves(acccurvedata);     //调用绘制曲线函数
                    finalvalue[11]=0;             //每一次只保留一下***
                }
            }

        }

        /// <summary>
        /// 跨线程调用不是很了解，如何更好的解决数据显示的问题？？难道真的要一句一句写？？太麻烦了？
        /// </summary>
        /// <param name="finalvalue"></param>
        public  void datadisp(double[] finalvalue)
        {
            this.xacctxt.Invoke    //所有显示数据都要用这种方法吗？？
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.xacctxt.Text = Convert.ToString(finalvalue[0]);
                    //  this.xacctxt.Text = finalvalue[0].ToString("#.00000");
                  }

                )
            );
            this.yacctxt.Invoke    
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.yacctxt.Text = Convert.ToString(finalvalue[1]);
                  }

                )
            );
            this.zacctxt.Invoke
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.zacctxt.Text = Convert.ToString(finalvalue[2]);
                  }

                )
            );
            this.xgrytxt.Invoke    //所有显示数据都要用这种方法吗？？
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.xgrytxt.Text = Convert.ToString(finalvalue[3]);
                  }

                )
            );
            this.ygrytxt.Invoke
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.ygrytxt.Text = Convert.ToString(finalvalue[4]);
                  }

                )
            );
            this.zgrytxt.Invoke
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.zgrytxt.Text = Convert.ToString(finalvalue[5]);
                  }

                )
            );
            this.xmagtxt.Invoke    //所有显示数据都要用这种方法吗？？
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.xmagtxt.Text = Convert.ToString(finalvalue[6]);
                  }

                )
            );
            this.ymagtxt.Invoke
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.ymagtxt.Text = Convert.ToString(finalvalue[7]);
                  }

                )
            );
            this.zmagtxt.Invoke
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.zmagtxt.Text = Convert.ToString(finalvalue[8]);
                  }

                )
            );
            this.rolltxt.Invoke    //所有显示数据都要用这种方法吗？？
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.rolltxt.Text = Convert.ToString(finalvalue[9]);
                  }

                )
            );
            this.pitchtxt.Invoke
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.pitchtxt.Text = Convert.ToString(finalvalue[10]);
                  }

                )
            );
            this.yawtxt.Invoke
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.yawtxt.Text = Convert.ToString(finalvalue[11]);
                  }

                )
            );
        }



        /// <summary>
        /// 绘制出数据曲线图，如何将曲线动态显示变化，即一直表示向右移的过程。
        /// 画图太麻烦了，太占时间了效率低下
        /// 绘制的时候还要考虑加速度的正负影响
        /// 人眼还是能够看出画图时图像的移动
        /// </summary>
        public void drawcurves(double datatmp)    //不需要用很精确的浮点型数据值来绘制曲线，我只需要绘制出曲线的
        {                                          //各点之间值的相对变化及位置关系
            Pen mypen;
            Graphics mygf;
            mygf = accpictbox.CreateGraphics();
            mygf.Clear(Color.AntiqueWhite);  //每次进来将上次的图清掉重画
           // double[] accfigdata = new double[100];  //先暂时假设容量这么大,设为全局变量
            // Array.Copy(datatmp,0,accfigdata,len,datatmp.Length);
            // len += datatmp.Length;                //动态更新曲线
            datatmp = Convert.ToDouble(datatmp.ToString("#.00"));    //保留小数点后两位数据，只描绘出大致的变化趋势
           // accfigdata[len] = datatmp;
           // len += 1; 
            //mylist.Add(datatmp);                //更新数据长度
            int timeaxis = 0;
            int accaxis = 0;
            mylist.Insert(0, datatmp);
            mypen = new Pen(Color.Black, 1);
            int x_initial = 5;
            int y_initial = 5;
            Point point1 = new Point(0, 0);
            Point point2 = new Point(0, 0);
            Point point3 = new Point(0, 0);
            Rectangle rect = new Rectangle(x_initial-2,accpictbox.Size.Height-95-2, 4, 4);
            point2 = point1;
            timeaxis = accpictbox.Size.Width;
            accaxis = accpictbox.Size.Height-50;    ////
            Point p1 = new Point(5, accaxis - 5);    //是原点
            Point p2 = new Point(timeaxis - 5, accaxis - 5);   //横轴
            Point p3 = new Point(x_initial, y_initial);   //纵轴
            Point p4 = new Point(timeaxis - 8, accaxis - 8);
            Point p5 = new Point(timeaxis - 8, accaxis - 2);
            Point p6 = new Point(x_initial - 3, y_initial + 3);
            Point p7 = new Point(x_initial + 3, y_initial + 3);
            Point p8 = new Point(5,accpictbox.Size.Height-5);

            mygf.DrawLine(System.Drawing.Pens.Black, p3, p1);
            mygf.DrawLine(System.Drawing.Pens.Black, p1, p2);
            mygf.DrawLine(System.Drawing.Pens.Black, p8, p1);
            mygf.DrawLine(System.Drawing.Pens.Black, p2, p4);
            mygf.DrawLine(System.Drawing.Pens.Black, p2, p5);
            mygf.DrawLine(System.Drawing.Pens.Black, p3, p6);
            mygf.DrawLine(System.Drawing.Pens.Black, p3, p7);//坐标轴
            mygf.DrawEllipse(System.Drawing.Pens.Black,rect);
            //#region          //绘制虚线，用于标示,但为什么画面会抖？频率不稳定？？？
            //point2.X = x_initial;
            //point2.Y = x_initial;
            //for (point2.Y = accpictbox.Size.Height; point2.Y > y_initial; point2.Y -= 40)
            //{
            //    point2.X = x_initial;
            //    point3 = point2;
            //    for (point2.X = x_initial; point2.X < accpictbox.Size.Width - 10; point2.X++)
            //    {                                //绘制虚线
            //        mygf.DrawLine(mypen, point2, point3);
            //        point3 = point2;
            //        if ((point2.X % 5 == 0) && (point2.X != x_initial))
            //        {
            //            point2.X += 10;
            //            point3 = point2;
            //        }
            //    }
            //}
            //#endregion
            point1 = p1;
            Point[]  curve = new Point[mylist.Count];//???接下来建立时间与加速度的关系
            int i = 0;       //mylist中的数据应该倒序显示，绘制吧
            foreach (double element in mylist)      //????????????
            {
                point1.X = point1.X + 4*i;
                if (point1.X >= timeaxis)//坐标原点表示当前的时刻
                {
                    point1.X = 5;              //应该将List中元素清掉
                    i = 0;
                    mylist.Clear();
                  //  mygf.Clear(Color.Teal);
                    break;
                    // mylist.Remove(i);          //mylist满的话就将最右端数据删除 
                }
                else
                {
                    point1.Y = p1.Y - Convert.ToInt16(element * 4);
                    curve[i] = point1;
                    i++;
                }
                
            }
            mygf.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;//能够绘制平滑的曲线*****
            if(i>1)
                mygf.DrawCurve(mypen, curve);
            mygf.Dispose();
        }

        #region         //保存采集过来的数据
        private void savedatabtn_Click(object sender, EventArgs e)
        {
            serialPort1.Close();
            string fname = "";
            sfd.Filter = "文本文件(*.txt)|*.txt|C#文件(*.cs)|*.cs|所有文件|*.*|word文档(*.doc)|*.doc";
            sfd.FilterIndex = 2;
            sfd.RestoreDirectory = true;
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                fname = sfd.FileName;
                savefile(fname);
            }
        }

        protected void savefile(string filename)
        {      
            try
            {
                Stream stream = File.OpenWrite(filename);
                using (StreamWriter writer = new StreamWriter(stream))
                {
                    writer.Write(richTextBox1.Text);
                }
            }
            catch (IOException ex)
            {
                MessageBox.Show(ex.Message, "Simple Editor", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }
        #endregion       

        private void Form1_Load_1(object sender, EventArgs e)
        {
            intial();
            //label1.Text = "t/s";
            //label2.Text = "m/s2";
            //label3.Text = "0";
            //drawaxis();          //执行了为什么不显示？？
        }

        public void intial()
        {
            label1.Text = "t/s";
            label2.Text = "m/s2";
            label3.Text = "0";
            label4.Text = "10";
            label5.Text = "-10";
        }
        //private void timer1_Tick(object sender, EventArgs e)
        //{
        //    drawaxis();             //1s钟触发一次
        //   // drawcurves();
        //}

        /// <summary>
        /// 窗体加载时候发生
        /// </summary>
        //public void drawaxis()
        //{
        //    Pen mypen;
        //    int x_initial = 5;
        //    int y_initial = 5;
        //    mypen = new Pen(Color.Black, 1);
        //    Graphics mygf;
        //    Point p1 = new Point(5, accpictbox.Size.Height - 5);    //是原点
        //    Point p2 = new Point(accpictbox.Size.Width - 5, accpictbox.Size.Height - 5);   //横轴
        //    Point p3 = new Point(x_initial, y_initial);   //纵轴
        //    Point p4 = new Point(accpictbox.Size.Width - 8, accpictbox.Size.Height - 8);
        //    Point p5 = new Point(accpictbox.Size.Width - 8, accpictbox.Size.Height - 2);
        //    Point p6 = new Point(x_initial - 3, y_initial + 3);
        //    Point p7 = new Point(x_initial + 3, y_initial + 3);
        //    mygf = accpictbox.CreateGraphics();

        //    Point point1 = new Point(0, 0);
        //    Point point2 = new Point(0, 0);
        //    Point[] curvepoint = { p3, p1, p2 };
        //    Rectangle rect = new Rectangle(0, 0, 5, 5);
        //    mygf.DrawLines(System.Drawing.Pens.Black, curvepoint);//这两条语句能实现画坐标，curvepoint相当于一个数组，可以顺序
        //    ///画下去
        //    mygf.DrawLine(System.Drawing.Pens.Black, p2, p4);
        //    mygf.DrawLine(System.Drawing.Pens.Black, p2, p5);
        //    mygf.DrawLine(System.Drawing.Pens.Black, p3, p6);
        //    mygf.DrawLine(System.Drawing.Pens.Black, p3, p7);//坐标轴
        //}

    }
}
